﻿#include "XmlDataUnit.h"
#include <QDir>
#include <QHash>
#include <QList>
#include <qlist.h>
#include <QTextStream>
#include <QFile>
#include <QDomComment>
#include <QtXml>

dXml::XmlDataUnit* dXml::XmlDataUnit::clonChildren(XmlDataUnit* xmlObj
	, XmlDataUnit* newObjParen) {
	dXml::XmlDataUnit* result = new dXml::XmlDataUnit;
	newObjParen->appendChildren(result);
	result->name = xmlObj->name;
	if( !xmlObj->children->isEmpty( ) ) {
		QList<XmlDataUnit*>::iterator iterator = xmlObj->children->begin( );
		QList<XmlDataUnit*>::iterator end = xmlObj->children->end( );
		for( ;iterator != end;++iterator ) {
			result->appendChildren(clonChildren(*iterator, result));
		}
	} else {
		result->setText(xmlObj->text);
	}
	if( !xmlObj->attrs->isEmpty( ) ) {
		auto iterator = xmlObj->attrs->begin( );
		auto end = xmlObj->attrs->end( );
		for( ;iterator != end;++iterator ) {
			result->attrs->insert(iterator.key( ), iterator.value( ));
		}
	}
	return result;
}

dXml::XmlDataUnit::XmlDataUnit( ) {
	children = new QList<XmlDataUnit*>;
	attrs = new QHash<QString, QString>;
}

dXml::XmlDataUnit::XmlDataUnit(const XmlDataUnit& xmlObj) {
	children = new QList<XmlDataUnit*>;
	attrs = new QHash<QString, QString>;
	if( !xmlObj.children->isEmpty( ) ) {
		int count = xmlObj.children->count( );
		for( int index = 0;index < count;++index ) {
			this->appendChildren(clonChildren(xmlObj.children->at(index), this));
		}
		/*QList<XmlDataUnit*>::iterator iterator = xmlObj.children->begin( );
		QList<XmlDataUnit*>::iterator end = xmlObj.children->end( );
		for( ;iterator != end;++iterator ) {
			XmlDataUnit* children = clonChildren(*iterator, this);
			this->appendChildren(children);
		}*/
	}
	if( !xmlObj.attrs->isEmpty( ) ) {
		auto iterator = xmlObj.attrs->begin( );
		auto end = xmlObj.attrs->end( );
		for( ;iterator != end;++iterator ) {
			this->attrs->insert(iterator.key( ), iterator.value( ));
		}
	}
	this->name = xmlObj.name;
	this->text = xmlObj.text;
}

dXml::XmlDataUnit& dXml::XmlDataUnit::operator=(const XmlDataUnit& xmlObj) {
	children = new QList<XmlDataUnit*>;
	attrs = new QHash<QString, QString>;
	if( !xmlObj.children->isEmpty( ) ) {
		QList<XmlDataUnit*>::iterator iterator = xmlObj.children->begin( );
		QList<XmlDataUnit*>::iterator end = xmlObj.children->end( );
		for( ;iterator != end;++iterator ) {
			this->appendChildren(clonChildren(*iterator, this));
		}
	}
	if( !xmlObj.attrs->isEmpty( ) ) {
		auto iterator = xmlObj.attrs->begin( );
		auto end = xmlObj.attrs->end( );
		for( ;iterator != end;++iterator ) {
			attrs->insert(iterator.key( ), iterator.value( ));
		}
	}
	this->name = xmlObj.name;
	this->text = xmlObj.text;
	return *this;
}

/*
void dXml::XmlDataUnit::setComment(const QStringList& com) {
	this->comments = com;
}

void dXml::XmlDataUnit::setComment(const QString& com) {
	this->comments.clear( );
	comments << com;
}

void dXml::XmlDataUnit::appendComment(const QString& com) {
	comments << com;
}

void dXml::XmlDataUnit::appendComment(const QStringList& com) {
	comments << com;
}

const QString dXml::XmlDataUnit::getComment(const QString& jion) {
	return comments.join(jion);
}

const QString dXml::XmlDataUnit::getComment(const char*& jion) {
	return comments.join(jion);
}

const QString dXml::XmlDataUnit::getComment(const char& jion) {
	return comments.join(jion);
}
*/

dXml::XmlDataUnit::XmlDataUnit(const QString& Nodename): XmlDataUnit{ } {
	this->name = Nodename;
}

void dXml::XmlDataUnit::sort(std::function<bool(XmlDataUnit* left
	, XmlDataUnit* right)> sorFun) {
	qSort(children->begin( ), children->end( ), sorFun);
}

/*
void dXml::XmlDataUnit::setComment(QString& comment) {
	comments.clear( );
	comments << comment;
}

void dXml::XmlDataUnit::appendComment(QString& comment) {
	comments << comment;
}
*/

void dXml::XmlDataUnit::clear( ) {
	while( !this->children->isEmpty( ) ) {
		XmlDataUnit* unit = this->children->last( );
		delete unit;
		this->children->removeLast( );
	}
	attrs->clear( );
	this->text = "";
}

const QHash<QString, QList<dXml::XmlDataUnit*>> dXml::XmlDataUnit::getXpath( ) {
	QHash<QString, QList<dXml::XmlDataUnit*>> result;
	QList<dXml::XmlDataUnit*> value;
	value.append(this);
	result.insert(this->name, value);
	if( !children->isEmpty( ) ) {
		QList<XmlDataUnit*>::iterator childrenIterator = children->begin( );
		QList<XmlDataUnit*>::iterator childrenEnd = children->end( );
		for( ;childrenIterator != childrenEnd;++childrenIterator ) {
			auto xpathUnits = ( *childrenIterator )->getXpath( );
			QHash<QString, QList<XmlDataUnit*>>::iterator xpathBegin = xpathUnits.begin( );
			QHash<QString, QList<XmlDataUnit*>>::iterator xpathEnd = xpathUnits.end( );
			for( ;xpathBegin != xpathEnd;++xpathBegin ) {
				QString key = this->name + "." + xpathBegin.key( );
				QList<XmlDataUnit*> xmlDataUnits = result.value(key);
				xmlDataUnits.append(this);
				result.insert(key, xmlDataUnits);
			}
		}
	}
	return result;
}

void dXml::XmlDataUnit::setParen(XmlDataUnit* paren
	, int parentIndex) {
	XmlDataUnit* unit = this->paren;
	// 删除父类关联子类信息
	if( unit != Q_NULLPTR ) {
		QList<XmlDataUnit*>* xmlDataUnits = unit->children;
		if( !xmlDataUnits->isEmpty( ) ) {
			int parenLen = xmlDataUnits->length( );
			int index = 0;
			for( ;index < parenLen;++index ) {
				if( xmlDataUnits->at(index) == this )
					break;
			}
			xmlDataUnits->removeAt(index);
		}
	}
	this->paren = paren;
	if( paren == Q_NULLPTR )
		return;
	QList<XmlDataUnit*>* parentList = paren->children;
	auto&& length = parentList->length( ) - 1;
	if( parentIndex < 0 || parentIndex > length )
		parentList->append(this);
	else
		parentList->insert(parentIndex, this);
}

dXml::XmlDataUnit* dXml::XmlDataUnit::getParen( ) const {
	return paren;
}

dXml::XmlDataUnit* dXml::XmlDataUnit::findChildren(const QString& name) {
	QList<XmlDataUnit*>::iterator iterator = children->begin( );
	QList<XmlDataUnit*>::iterator end = children->end( );
	for( ;iterator != end;++iterator ) {
		XmlDataUnit* xmlDataUnit = *iterator;
		if( name == xmlDataUnit->name ) {
			return xmlDataUnit;
		}
	}
	return Q_NULLPTR;
}

QList<dXml::XmlDataUnit*> dXml::XmlDataUnit::findChildrens(const QString& name) {
	QList<XmlDataUnit*> result;
	QList<XmlDataUnit*>::iterator iterator = children->begin( );
	QList<XmlDataUnit*>::iterator end = children->end( );
	for( ;iterator != end;++iterator ) {
		XmlDataUnit* xmlDataUnit = *iterator;
		if( name == xmlDataUnit->name ) {
			result.append(xmlDataUnit);
		}
	}
	return result;
}

QList<dXml::XmlDataUnit*>* dXml::XmlDataUnit::getChildrens( ) const {
	return this->children;
}

QString dXml::XmlDataUnit::nodeName( ) const {
	return name;
}

void dXml::XmlDataUnit::setName(const QString& name) {
	this->name = name;
}

QHash<QString, QString>* dXml::XmlDataUnit::getAttrs( ) const {
	return this->attrs;
}

QString& dXml::XmlDataUnit::getText( ) {
	return this->text;
}

void dXml::XmlDataUnit::appendAttr(const QString& attr
	, const QString& value) {
	this->attrs->insert(attr, value);
}

void dXml::XmlDataUnit::appendAttrs(QHash<QString, QString>& attrs) {
	QHash<QString, QString>::iterator iterator = attrs.begin( );
	QHash<QString, QString>::iterator end = attrs.end( );
	for( ;iterator != end;++iterator )
		this->attrs->insert(iterator.key( ), iterator.value( ));
}

void dXml::XmlDataUnit::appendAttrs(QList<QString>& attrs
	, QList<QString>& values) {
	QHash<QString, QString>::iterator end = this->attrs->end( );
	int length = attrs.length( );
	int index = 0;
	for( ;index < length;++ index ) {
		auto&& attr = attrs.at(index);
		auto&& value = values.at(index);
		this->attrs->insert(attr, value);
	}
}

bool dXml::XmlDataUnit::setText(const QString& text
	, bool clearChilder) {
	if( !children->isEmpty( ) )
		if( clearChilder ) {
			while( !children->isEmpty( ) ) {
				delete children->last( );
				children->removeLast( );
			}
		} else
			return false;
	this->text = text;
	return true;
}

void dXml::XmlDataUnit::appendChildren(XmlDataUnit* children
	, int index) {
	if( !this->children->contains(children) )
		children->setParen(this, index);
}

void dXml::XmlDataUnit::appendChildrens(QList<XmlDataUnit*>& childrens
	, int index) {
	auto&& length = children->length( ) - 1;
	if( index > length )
		index = length;
	while( ! childrens.isEmpty( ) ) {
		XmlDataUnit* children = childrens.last( );
		childrens.removeLast( );
		// this->children->insert(index, children);
		children->setParen(this);
	}
}

bool dXml::XmlDataUnit::save(QFile& file
	, const char* processingInstructionData
	, const char* processingInstructionTarget
	, int tabSize
	, QDomNode::EncodingPolicy edcoding) {
	QDomDocument qDomDocument;
	if( processingInstructionData == Q_NULLPTR )
		processingInstructionData = dXml::getProcessingInstructionData( );
	if( processingInstructionTarget == Q_NULLPTR )
		processingInstructionTarget = dXml::getProcessingInstructionTarget( );
	QDomProcessingInstruction instruction = qDomDocument.createProcessingInstruction(processingInstructionTarget, processingInstructionData);
	qDomDocument.appendChild(instruction);
	return save(file, qDomDocument, tabSize, edcoding);
}

bool dXml::XmlDataUnit::save(QFile& file
	, QDomDocument& qDomDocument
	, int tabSize
	, QDomNode::EncodingPolicy edcoding) {
	QDomNodeList list = qDomDocument.childNodes( );
	bool hasProcessingInstruction = false;
	int length = list.length( );
	for( int i = 0;i < length;++i ) {
		QDomNode domNode = list.at(i);
		if( domNode.isProcessingInstruction( ) ) {
			hasProcessingInstruction = true;
			break;
		}
	}
	if( !hasProcessingInstruction ) {
		QDomProcessingInstruction processingInstruction = qDomDocument.createProcessingInstruction(dXml::getProcessingInstructionTarget( ), dXml::getProcessingInstructionData( ));
		qDomDocument.insertBefore(processingInstruction, qDomDocument.firstChild( ));
	}
	if( !file.isOpen( ) )
		if( !file.open(QIODevice::WriteOnly | QIODevice::Unbuffered) )
			return false;
	qDomDocument.appendChild(dXml::toDocElement(qDomDocument, this));

	//输出到文件
	QTextStream out_stream(&file);
	qDomDocument.save(out_stream, tabSize, edcoding); //缩进4格
	return true;
}

bool dXml::XmlDataUnit::save(const QString& file
	, QDomDocument& qDomDocument
	, int tabSize
	, QDomNode::EncodingPolicy edcoding) {
	QDir dir;
	QFileInfo info(file);
	dir.mkpath(info.absolutePath( ));
	QFile writefile(file);
	bool saveResult = save(writefile, qDomDocument, tabSize, edcoding);
	writefile.close( );
	return saveResult;
}

bool dXml::XmlDataUnit::save(const QString& file
	, QDomDocument* qDomDocument
	, int tabSize
	, QDomNode::EncodingPolicy edcoding) {
	if( qDomDocument != Q_NULLPTR ) {
		return save(file, *qDomDocument, tabSize, edcoding);
	} else {
		QDomDocument document;
		return save(file, document, tabSize, edcoding);
	}
}

bool dXml::XmlDataUnit::save(const char* file
	, QDomDocument* qDomDocument
	, int tabSize
	, QDomNode::EncodingPolicy edcoding) {
	auto string = QString(file);
	return save(string, qDomDocument, tabSize, edcoding);
}

int dXml::XmlDataUnit::getChildrensSize( ) const {
	return children->length( );
}

dXml::XmlDataUnit::~XmlDataUnit( ) {
	QList<XmlDataUnit*>* dataUnits = this->children;
	while( !dataUnits->isEmpty( ) ) {
		XmlDataUnit* unit = this->children->last( );
		this->children->removeLast( );
		delete unit;
	}
	delete children;
	delete attrs;
	children = Q_NULLPTR;
	attrs = Q_NULLPTR;
}
